#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

#define SEMKEY   90
#define SHMKEY   91	

#define EMPTY 0 /* definizioni numeri dei semafori per la gestione del buffer */
#define FULL 1  
#define MUTEX 2

/* struttura per l'inserimento dei dati */
typedef struct record_anag {
	char first_name[20];
	char last_name[20];
	int age;
	char email[40];			
} message;
			 
struct sembuf wait_b,signal_b;  /* strutture per le operazioni sui semafori */			 
int semid; /* id per i semafori */
int shmid; /* id per la memoria condivisa */		 
			 
void sem_wait (int sem_number) {

/* inserire la procedura per l'esecuzione di una wait 
sul semaforo numero sem_number come fatto nell'esercizio ex2 */
	wait_b.sem_num = sem_number;
	if(semop(semid, &wait_b, 1) == -1){
		perror("Errore nella sem_wait()\n");
		semctl(semid, 0, IPC_RMID, 0);
		shmctl(shmid, IPC_RMID, 0);
		exit(1);
	}
}

void sem_signal (int sem_number) {

/* inserire la procedura per l'esecuzione di una signal sul 
semaforo numero sem_number come fatto per l'esercizio ex2 */
	signal_b.sem_num = sem_number;
	if(semop(semid, &signal_b, 1) == -1){
		perror("Errore nella sem_signal()\n");
		semctl(semid, 0, IPC_RMID, 0);
		shmctl(shmid, IPC_RMID, 0);
		exit(1);
	}

}			 		 


void exit_fnc() {
  /* definire la funzione che cattura il segnale inviato dal timer.
	La funzione deve rimuovere coda di messaggi e semafori
	e far terminare il programma. */
	semctl(semid, 0, IPC_RMID, 0);
	shmctl(shmid, IPC_RMID, 0);
	exit(0);
}
			 
int main(int argc, char *argv[]) {

	message *m;
	message *buf;
	int sem_stato;          /* stato del semaforo */
	union semun {           /* union per la gestione del controllo sui semafori */
				int val;
				struct semid_ds *buf;
				ushort *array;
	} st_sem;
	
	m = (message *) malloc(sizeof(message));
	
	wait_b.sem_num=signal_b.sem_num=0;	/* inizializzazione struttura operazioni */
	wait_b.sem_op=-1;										/* la 'wait' sui semafori richiede sem_op < 0 */
	signal_b.sem_op=1;									/* la 'signal' sui semafori richiede sem_op > 0 */

	wait_b.sem_flg=signal_b.sem_flg=SEM_UNDO; /* flag di undo */
	st_sem.val=1;															/* assegnazione valore iniziale dei semafori */

	
	/* creare 3 semafori (EMPTY, FULL E MUTEX) con la semget() */
	semid = semget(SEMKEY, 3, 0666 | IPC_EXCL | IPC_CREAT);
	if(semid == -1){
		perror("Errore nella creazione del set di semafori\n");
		exit(1);
	}


	/* assegnare i valori iniziali dei semafori 
		 EMPTY = 1
		 MUTEX = 1
		 FULL = 0
	*/
	
	sem_stato = semctl(semid, MUTEX, SETVAL, st_sem);
	if(sem_stato == -1){
		perror("Errore nell'inizializzazione del semaforo MUTEX\n");
		semctl(semid, 0, IPC_RMID, 0);
		exit(1);
	}
	
	st_sem.val = 0;
	sem_stato = semctl(semid, FULL, SETVAL, st_sem);
	if(sem_stato == -1){
		perror("Errore nell'inizializzazione del semaforo FULL\n");
		semctl(semid, 0, IPC_RMID, 0);
		exit(1);
	}
	
	st_sem.val = 1;
	sem_stato = semctl(semid, EMPTY, SETVAL, st_sem);
	if(sem_stato == -1){
		perror("Errore nell'inizializzazione del semaforo EMPTY\n");
		semctl(semid, 0, IPC_RMID, 0);
		exit(1);
	}


	/*creare la memoria condivisa usando la shmget() */
	shmid = shmget(SHMKEY, sizeof(message), 0666 | IPC_CREAT);
	if(shmid == -1){
		perror("Errore nella creazione della memoria condivisa\n");
		semctl(semid, 0, IPC_RMID, 0);
		exit(1);
	}

	/* montare l'area di memoria condivisa nell'area dati del processo
	a partire dal primo indirizzo disponibile tramite la shmat() */
	
	if((buf = (message *) shmat(shmid, 0, 0666)) == (message *) -1){
		perror("Errore nella shmat()\n");
		semctl(semid, 0, IPC_RMID, 0);
		shmctl(shmid, IPC_RMID, 0);
		exit(1);
	}
	

	/* registrare il server per la esecuzione della funzione exit_fnc()
	   al momento della ricezione del segnale SIGALRM.
		 Usare la funzione signal() */
	signal(SIGALRM, exit_fnc);

	/*Leggere ciclicamente dalla memoria condivisa il messaggio inserito
	  dal client */
	while(1) {
		/* far partire l'allarme con la alarm() */
		alarm(30);
		
		/* leggere dalla memoria condivisa proteggendo 
		   la lettura con i semafori FULL, MUTEX e EMPTY come
			 nel caso del produttore-consumatore */
		sem_wait(FULL);
		
		sem_wait(MUTEX);
		
		/* disarmare l'allarme */	
		alarm(0);	
		
		memcpy(m, buf, sizeof(message));
		
		sem_signal(MUTEX);
		
		sem_signal(EMPTY);
		
		
		/* stampare a video il messaggio letto nella memoria condivisa */
		printf("\nRicevuto un messaggio:\n");
		printf("Nome: %s\n", m->first_name);
		printf("Cognome: %s\n", m->last_name);
		printf("Eta': %d\n", m->age);
		printf("Email: %s\n", m->email);
	}
	return 1;

}

